﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace QrCode.FountainUtils
{
    public class FountainCode
    {
        // 数据分块
        public List<DataPacket> SplitData(byte[] originalData, int blockSize)
        {
            try
            {
                // 创建用于存储分块后的数据块列表
                List<DataPacket> blocks = new List<DataPacket>();
                // 遍历原始数据，以 blockSize 为步长进行分块
                for (int i = 0; i < originalData.Length; i += blockSize)
                {
                    // 计算剩余数据长度
                    int remaining = originalData.Length - i;
                    // 确定当前要提取的数据块大小，取 blockSize 和剩余长度中的较小值
                    int sizeToTake = Math.Min(blockSize, remaining);
                    // 创建新的字节数组用于存储当前数据块的数据
                    byte[] blockData = new byte[sizeToTake];
                    // 将原始数据中的一部分复制到当前数据块的字节数组中
                    Array.Copy(originalData, i, blockData, 0, sizeToTake);
                    // 将当前数据块添加到列表中
                    blocks.Add(new DataPacket { Data = blockData });
                }
                // 返回分块后的列表
                return blocks;
            }
            catch (Exception ex)
            {
                // 如果出现异常，输出错误信息并返回空列表
                Console.WriteLine($"Error in SplitData: {ex.Message}");
                return new List<DataPacket>();
            }
        }

        // 编码生成
        public List<DataPacket> Encode(List<DataPacket> originalBlocks)
        {
            try
            {
                // 创建用于存储编码后的数据块列表
                List<DataPacket> encodedBlocks = new List<DataPacket>();
                // 创建随机数生成器
                Random random = new Random();
                // 使用并行处理生成编码块
                while (encodedBlocks.Count < originalBlocks.Count * 2)
                {
                    // 随机选择一定数量的原始数据块
                    List<DataPacket> selectedBlocks = SelectRandomBlocks(originalBlocks, random.Next(2, originalBlocks.Count));
                    // 将选中的原始数据块进行异或操作生成新的编码块
                    byte[] combinedData = CombineBlocks(selectedBlocks);
                    // 将编码块添加到列表中
                    encodedBlocks.Add(new DataPacket { Data = combinedData });
                }
                return encodedBlocks;
            }
            catch (Exception ex)
            {
                // 如果出现异常，输出错误信息并返回空列表
                Console.WriteLine($"Error in GenerateEncodedBlocks: {ex.Message}");
                return new List<DataPacket>();
            }
        }

        private List<DataPacket> SelectRandomBlocks(List<DataPacket> blocks, int count)
        {
            try
            {
                // 创建用于存储选中的数据块列表
                List<DataPacket> selected = new List<DataPacket>();
                // 创建随机数生成器
                Random random = new Random();
                // 随机选择 count 个数据块
                for (int i = 0; i < count; i++)
                {
                    // 生成随机索引
                    int index = random.Next(blocks.Count);
                    // 将随机索引对应的数据块添加到选中列表中
                    selected.Add(blocks[index]);
                }
                // 返回选中的数据块列表
                return selected;
            }
            catch (Exception ex)
            {
                // 如果出现异常，输出错误信息并返回空列表
                Console.WriteLine($"Error in SelectRandomBlocks: {ex.Message}");
                return new List<DataPacket>();
            }
        }

        private byte[] CombineBlocks(List<DataPacket> blocks)
        {
            try
            {
                if (blocks.Count == 0)
                    return new byte[0];

                // 以第一个数据块作为初始组合结果
                byte[] combined = blocks[0].Data;
                // 对选中的数据块进行异或操作，逐步组合数据
                for (int i = 1; i < blocks.Count; i++)
                {
                    combined = XorByteArrays(combined, blocks[i].Data);
                }
                // 返回组合后的字节数组
                return combined;
            }
            catch (Exception ex)
            {
                // 如果出现异常，输出错误信息并返回空字节数组
                Console.WriteLine($"Error in CombineBlocks: {ex.Message}");
                return new byte[0];
            }
        }

        private byte[] XorByteArrays(byte[] a, byte[] b)
        {
            try
            {
                // 如果两个字节数组长度不同，抛出异常
                if (a.Length != b.Length)
                    throw new ArgumentException("Byte arrays must be of equal length.");

                // 创建结果字节数组
                byte[] result = new byte[a.Length];
                // 对两个字节数组进行异或操作
                for (int i = 0; i < a.Length; i++)
                {
                    result[i] = (byte)(a[i] ^ b[i]);
                }
                // 返回异或结果
                return result;
            }
            catch (Exception ex)
            {
                // 如果出现异常，输出错误信息并返回空字节数组
                Console.WriteLine($"Error in XorByteArrays: {ex.Message}");
                return new byte[0];
            }
        }

        // 解码
        public List<DataPacket> Decode(List<DataPacket> encodedBlocks, int originalBlockCount)
        {
            try
            {
                // 创建用于存储解码后的数据块列表
                List<DataPacket> decodedBlocks = new List<DataPacket>();
                // 不断尝试解码，直到解码出所有原始数据块
                while (decodedBlocks.Count < originalBlockCount)
                {
                    // 随机选择一定数量的编码块
                    List<DataPacket> selectedBlocks = SelectRandomBlocks(encodedBlocks, originalBlockCount);
                    // 将选中的编码块进行组合
                    byte[] combinedData = CombineBlocks(selectedBlocks);
                    // 将组合后的结果添加到解码后的数据块列表中
                    decodedBlocks.Add(new DataPacket { Data = combinedData });
                    // 检查是否已经解码出所有原始数据块
                    if (IsDecoded(decodedBlocks, originalBlockCount))
                        break;
                }
                // 返回解码后的列表
                return decodedBlocks;
            }
            catch (Exception ex)
            {
                // 如果出现异常，输出错误信息并返回空列表
                Console.WriteLine($"Error in Decode: {ex.Message}");
                return new List<DataPacket>();
            }
        }

        private bool IsDecoded(List<DataPacket> decodedBlocks, int originalBlockCount)
        {
            try
            {
                // 如果解码后的数据块数量小于原始数据块数量，返回 false
                if (decodedBlocks.Count < originalBlockCount)
                    return false;

                // 检查解码后的数据块是否唯一
                for (int i = 0; i < decodedBlocks.Count; i++)
                {
                    bool isUnique = true;
                    for (int j = i + 1; j < decodedBlocks.Count; j++)
                    {
                        // 如果发现两个数据块相同，设置标志为 false
                        if (Enumerable.SequenceEqual(decodedBlocks[i].Data, decodedBlocks[j].Data))
                        {
                            isUnique = false;
                            break;
                        }
                    }
                    // 如果有重复的数据块，返回 false
                    if (!isUnique)
                        return false;
                }
                // 如果所有数据块都是唯一的，返回 true
                return true;
            }
            catch (Exception ex)
            {
                // 如果出现异常，输出错误信息并返回 false
                Console.WriteLine($"Error in IsDecoded: {ex.Message}");
                return false;
            }
        }
    }
}
